﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace ScrumPilot.Domain
{
    public abstract class RepositoryBase<T> where T : class
    {
        internal DBScrumPilotEntities DataContext;
        internal DbSet<T> Dbset;

        protected RepositoryBase()
        {
            this.DataContext = new DBScrumPilotEntities();
        }

        protected RepositoryBase(DBScrumPilotEntities context)
        {
            this.DataContext = context;
            this.Dbset = context.Set<T>();
        }

        public virtual IQueryable<T> All()
        {
            return Dbset.AsQueryable();
        }

        public virtual IQueryable<T> Filter(Expression<Func<T, bool>> predicate)
        {
            return Dbset.Where(predicate).AsQueryable();
        }

        public virtual IQueryable<T> Filter(Expression<Func<T, bool>> filter, out int total, int index = 0, int size = 50)
        {
            int skipCount = index * size;
            var resetSet = filter != null ? Dbset.Where(filter).AsQueryable() : Dbset.AsQueryable();
            resetSet = skipCount == 0 ? resetSet.Take(size) : resetSet.Skip(skipCount).Take(size);
            total = resetSet.Count();
            return resetSet.AsQueryable();
        }

        public bool Contains(Expression<Func<T, bool>> predicate)
        {
            return Dbset.Count(predicate) > 0;
        }

// ReSharper disable MethodOverloadWithOptionalParameter
        public virtual IQueryable<T> Get(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
// ReSharper restore MethodOverloadWithOptionalParameter
            string includeProperties = "")
        {
            IQueryable<T> query = Dbset;

            if (filter != null)
            {
                query = query.Where(filter);
            }

            query = includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Aggregate(query, (current, includeProperty) => current.Include(includeProperty));

            return orderBy != null ? orderBy(query) : query;
        }

        public virtual void Add(T entity)
        {
            Dbset.Add(entity);
        }

        public virtual void Update(T entity)
        {
            Dbset.Attach(entity);
            DataContext.Entry(entity).State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            Dbset.Remove(entity);
        }

        public virtual void Delete(Expression<Func<T, bool>> where)
        {
            IEnumerable<T> objects = Dbset.Where(where).AsEnumerable();
            foreach (T obj in objects)
                Dbset.Remove(obj);
        }

        public virtual T Find(params object[] keys)
        {
            return Dbset.Find(keys);
        }

        public virtual T Find(Expression<Func<T, bool>> predicate)
        {
            return Dbset.FirstOrDefault(predicate);
        }

        public virtual T GetById(String id)
        {
            return Dbset.Find(id);
        }

        public virtual T GetById(long id)
        {
            return Dbset.Find(id);
        }

        public virtual T GetById(Guid id)
        {
            return Dbset.Find(id);
        }

        public virtual IEnumerable<T> GetAll()
        {
            return Dbset.ToList();
        }

        public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
        {
            return Dbset.Where(where).ToList();
        }

        public T Get(Expression<Func<T, bool>> where)
        {
            return Dbset.Where(where).FirstOrDefault();
        }

        public virtual T GetOne()
        {
            return Dbset.FirstOrDefault();
        }
    }
}
